/**
 * API工具类，封装所有API请求
 */
class Api {
    /**
     * 发送带有JWT Token的请求
     * @param {string} url - 请求URL
     * @param {Object} options - 请求选项
     * @returns {Promise} - 返回Promise
     */
    static async fetchWithToken(url, options = {}) {
        // 获取JWT Token
        const token = sessionStorage.getItem('jwtToken');
        
        // 设置默认headers
        if (!options.headers) {
            options.headers = {};
        }
        
        // 如果有token则添加到请求头
        if (token) {
            options.headers['Authorization'] = `Bearer ${token}`;
        } else {
            console.warn('JWT令牌不存在，这可能导致请求被拒绝');
        }
        
        try {
            const response = await fetch(url, options);
            
            // 处理未授权错误（例如token过期）
            if (response.status === 401) {
                let errorMessage = '您的登录已失效，请重新登录';
                let errorTitle = '登录失效';
                let redirectNow = true;
                let errorType = 'session_expired'; // 默认错误类型
                
                try {
                    const responseText = await response.text();
                    // 尝试解析为JSON
                    try {
                        const errorData = JSON.parse(responseText);
                        
                        // 检查是否是账号在其他地方登录
                        if (errorData.error === 'token_invalidated') {
                            console.error('账号在其他地方登录');
                            errorTitle = '账号已在其他设备登录';
                            errorMessage = '您的账号已在其他设备登录，请重新登录';
                            errorType = 'account_elsewhere';
                        } else if (errorData.error === 'token_expired') {
                            console.error('令牌已过期');
                            errorTitle = '登录已过期';
                            errorMessage = '您的登录已过期，请重新登录';
                            errorType = 'token_expired';
                        } else if (errorData.error === 'token_refreshed') {
                            console.warn('令牌已被刷新，正在尝试重新获取新令牌');
                            // 尝试获取新的token
                            try {
                                const refreshResult = await this.refreshToken();
                                if (refreshResult) {
                                    // 如果刷新成功，重试原请求
                                    console.info('令牌刷新成功，重试原请求');
                                    // 重新构建选项，使用新token
                                    const newToken = sessionStorage.getItem('jwtToken');
                                    options.headers['Authorization'] = `Bearer ${newToken}`;
                                    // 重试请求
                                    const retryResponse = await fetch(url, options);
                                    return retryResponse;
                                } else {
                                    // 刷新失败，显示错误
                                    errorTitle = '会话已刷新失败';
                                    errorMessage = '无法恢复您的会话，请重新登录';
                                    errorType = 'refresh_failed';
                                }
                            } catch (refreshError) {
                                console.error('刷新令牌失败:', refreshError);
                                errorTitle = '会话恢复失败';
                                errorMessage = '会话恢复失败，请重新登录';
                                errorType = 'refresh_failed';
                            }
                        } else {
                            console.error('令牌无效或会话已过期');
                            errorTitle = '登录已失效';
                            errorMessage = '您的登录状态已失效，请重新登录';
                            errorType = 'token_invalid';
                        }
                    } catch (e) {
                        // 如果不是JSON，使用通用错误消息
                        console.error('无法解析服务器响应:', responseText);
                    }
                } catch (e) {
                    console.error('读取服务器响应失败:', e);
                }
                
                // 保存错误信息到会话存储，用于登录页面显示
                sessionStorage.setItem('loginErrorReason', errorMessage);
                
                // 使用增强的自定义消息提示（如果可用）
                if (typeof window.showAuthModal === 'function') {
                    // 使用新的增强版认证弹窗
                    window.showAuthModal(errorTitle, errorMessage, errorType);
                    redirectNow = false;
                }
                // 兼容旧版消息弹窗
                else if (typeof window.showMessage === 'function') {
                    window.showMessage(errorTitle, errorMessage, 'error');
                    
                    // 设置短暂延迟后再重定向，让用户有时间看到消息
                    redirectNow = false;
                    setTimeout(() => {
                        // 清除会话存储中的登录信息
                        this.clearAuthSession();
                        
                        // 重定向到登录页
                        window.location.href = '/login.html';
                    }, 3000); // 3秒后重定向，增加时间让用户看清提示
                }
                
                // 如果没有自定义消息弹窗，立即重定向
                if (redirectNow) {
                    // 清除会话存储中的登录信息
                    this.clearAuthSession();
                    
                    // 重定向到登录页
                    window.location.href = '/login.html';
                }
                
                return null;
            }
            
            return response;
        } catch (error) {
            console.error('请求出错:', error);
            
            // 使用自定义消息提示（如果可用）
            if (typeof window.showMessage === 'function') {
                window.showMessage('网络错误', '请求失败，请检查您的网络连接', 'error');
            }
            
            throw error;
        }
    }
    
    /**
     * 清除所有认证相关的会话数据
     */
    static clearAuthSession() {
        sessionStorage.removeItem('isLoggedIn');
        sessionStorage.removeItem('jwtToken');
        sessionStorage.removeItem('userFullName');
        sessionStorage.removeItem('userRole');
    }
    
    /**
     * 刷新用户令牌
     * 调用专门的刷新令牌API获取新的Token
     * @returns {Promise<boolean>} 刷新成功返回true，失败返回false
     */
    static async refreshToken() {
        try {
            console.log('正在尝试刷新令牌...');
            const oldToken = sessionStorage.getItem('jwtToken');
            
            // 如果没有旧令牌，则无法刷新
            if (!oldToken) {
                console.warn('没有有效的令牌可以刷新');
                return false;
            }
            
            // 使用专门的刷新令牌API
            const response = await fetch('/api/refresh-token', {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${oldToken}`
                }
            });
            
            if (response.ok) {
                const data = await response.json();
                if (data.success && data.token) {
                    // 保存新令牌和用户信息
                    sessionStorage.setItem('jwtToken', data.token);
                    sessionStorage.setItem('userFullName', data.fullName || data.username);
                    sessionStorage.setItem('userRole', data.role);
                    console.log('令牌刷新成功');
                    return true;
                }
            }
            
            console.warn('令牌刷新失败:', response.status);
            return false;
        } catch (error) {
            console.error('刷新令牌时出错:', error);
            return false;
        }
    }
    
    /**
     * 获取当前用户信息
     */
    static async getCurrentUser() {
        const response = await this.fetchWithToken('/api/user/info');
        if (response && response.ok) {
            return await response.json();
        }
        return null;
    }
    
    /**
     * 更新用户真实姓名
     * @param {string} fullName - 新的真实姓名
     */
    static async updateFullName(fullName) {
        const response = await this.fetchWithToken('/api/update-fullname', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: `fullName=${encodeURIComponent(fullName)}`
        });
        
        if (response && response.ok) {
            return await response.json();
        }
        return null;
    }
    
    /**
     * 用户登出
     */
    static async logout() {
        const response = await this.fetchWithToken('/api/logout', {
            method: 'POST'
        });
        
        // 清除会话存储
        sessionStorage.removeItem('isLoggedIn');
        sessionStorage.removeItem('jwtToken');
        sessionStorage.removeItem('userFullName');
        sessionStorage.removeItem('userRole');
        
        return response && response.ok;
    }
    
    /**
     * 检查用户是否为管理员
     */
    static isAdmin() {
        const role = sessionStorage.getItem('userRole');
        return role === 'ROLE_ADMIN';
    }
    
    /**
     * 根据用户名获取真实姓名
     * @param {string} username - 用户名
     * @returns {Promise<string>} 真实姓名或原用户名
     */
    static async getRealNameByUsername(username) {
        try {
            const response = await this.fetchWithToken(`/api/user/real-name?username=${encodeURIComponent(username)}`);
            if (response && response.ok) {
                const data = await response.json();
                return data.realName || username;
            }
            return username;
        } catch (error) {
            console.error('获取真实姓名失败:', error);
            return username;
        }
    }
    
    /**
     * 执行用户名到真实姓名的数据迁移（仅管理员）
     * @returns {Promise<Object>} 迁移结果
     */
    static async migrateUsernames() {
        if (!this.isAdmin()) {
            console.error('非管理员用户无法执行此操作');
            return { success: false, message: '权限不足' };
        }
        
        try {
            const response = await this.fetchWithToken('/api/admin/tools/migrate-names', {
                method: 'POST'
            });
            
            if (response && response.ok) {
                return await response.json();
            }
            
            return { 
                success: false, 
                message: '迁移失败：' + (response ? response.statusText : '未知错误') 
            };
        } catch (error) {
            console.error('执行数据迁移失败:', error);
            return { success: false, message: '迁移失败：' + error.message };
        }
    }
    
    /**
     * 获取物料列表
     * @returns {Promise<Array>} 物料列表
     */
    static async getMaterials() {
        const response = await this.fetchWithToken('/api/materials');
        if (response && response.ok) {
            return await response.json();
        }
        return [];
    }
    
    /**
     * 上传物料
     * @param {FormData} formData - 包含文件和描述的表单数据
     * @returns {Promise<Object>} 上传结果
     */
    static async uploadMaterial(formData) {
        try {
            const response = await this.fetchWithToken('/api/materials/upload', {
                method: 'POST',
                body: formData
            });
            
            if (response && response.ok) {
                return await response.json();
            }
            
            // 处理非OK响应
            if (response) {
                // 尝试解析错误响应
                try {
                    const errorData = await response.json();
                    return {
                        success: false,
                        message: errorData.message || `上传失败 (${response.status})`,
                        error: errorData.error || 'request_failed',
                        status: response.status
                    };
                } catch (e) {
                    // 响应不是JSON格式
                    return {
                        success: false,
                        message: `上传失败：${response.statusText || '服务器错误'}`,
                        error: 'server_error',
                        status: response.status
                    };
                }
            }
            
            return {
                success: false,
                message: '网络错误，请检查连接',
                error: 'network_error'
            };
        } catch (error) {
            console.error('上传物料出错:', error);
            
            // 重新包装错误对象以便前端处理
            const wrappedError = new Error(error.message || '上传失败，请稍后重试');
            wrappedError.response = error.response;
            wrappedError.originalError = error;
            throw wrappedError;
        }
    }
    
    /**
     * 删除物料
     * @param {number} id - 物料ID
     * @returns {Promise<Object>} 删除结果
     */
    static async deleteMaterial(id) {
        const response = await this.fetchWithToken(`/api/materials/${id}`, {
            method: 'DELETE'
        });
        
        if (response && response.ok) {
            return await response.json();
        }
        return null;
    }
    
    /**
     * 获取物料下载链接
     * @param {number} id - 物料ID
     * @returns {string} 下载链接
     */
    static getMaterialDownloadUrl(id) {
        return `/api/materials/download/${id}`;
    }
    
    /**
     * 获取物料预览链接
     * @param {number} id - 物料ID
     * @returns {string} 预览链接
     */
    static getMaterialPreviewUrl(id) {
        return `/api/materials/preview/${id}`;
    }
    
    /**
     * 在新窗口预览物料文件
     * 根据文件类型提供不同的预览方式
     * @param {number} id - 物料ID 
     * @param {string} filename - 文件名（可选，用于显示）
     * @param {string} fileType - 文件MIME类型（可选，用于确定预览方式）
     * @returns {Promise<boolean>} 是否成功打开预览
     */
    static async previewMaterial(id, filename = '', fileType = '') {
        try {
            const token = sessionStorage.getItem('jwtToken');
            const url = this.getMaterialPreviewUrl(id);
            
            // 验证登录状态
            if (!token) {
                if (typeof window.showAuthModal === 'function') {
                    window.showAuthModal('需要登录', '请先登录后再预览文件', 'login_required');
                    return false;
                } else if (typeof window.showMessage === 'function') {
                    window.showMessage('需要登录', '请先登录后再预览文件', 'warning');
                    setTimeout(() => {
                        window.location.href = '/login.html?redirect=' + encodeURIComponent(window.location.href);
                    }, 2000);
                    return false;
                } else {
                    alert('请先登录后再预览文件');
                    window.location.href = '/login.html';
                    return false;
                }
            }
            
            // 显示全屏加载对话框
            let loadingDialog = document.getElementById('previewLoadingDialog');
            if (!loadingDialog) {
                loadingDialog = document.createElement('div');
                loadingDialog.id = 'previewLoadingDialog';
                loadingDialog.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4';
                loadingDialog.innerHTML = `
                    <div class="bg-white rounded-lg shadow-xl w-full max-w-md p-6 text-center">
                        <div class="animate-spin mx-auto h-16 w-16 border-4 border-blue-500 border-t-transparent rounded-full mb-4"></div>
                        <h3 class="text-lg font-medium text-gray-900 mb-2" id="previewLoadingText">正在加载预览...</h3>
                        <p class="text-sm text-gray-500 mb-4" id="previewLoadingSubtext">${filename || '准备文件中'}</p>
                        <button id="cancelPreviewBtn" class="px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300 focus:outline-none transition duration-150">
                            取消
                        </button>
                    </div>
                `;
                document.body.appendChild(loadingDialog);
            } else {
                loadingDialog.classList.remove('hidden');
                document.getElementById('previewLoadingText').textContent = '正在加载预览...';
                document.getElementById('previewLoadingSubtext').textContent = filename || '准备文件中';
            }
            
            // 设置取消按钮事件
            let abortController = new AbortController();
            const cancelPreviewBtn = document.getElementById('cancelPreviewBtn');
            if (cancelPreviewBtn) {
                cancelPreviewBtn.onclick = () => {
                    abortController.abort();
                    loadingDialog.classList.add('hidden');
                    if (typeof window.showMessage === 'function') {
                        window.showMessage('操作取消', '文件预览已取消', 'info');
                    }
                };
            }
            
            // 检查文件类型是否支持预览
            const fileExtension = filename ? filename.split('.').pop().toLowerCase() : '';
            
            // 更新加载提示
            const previewLoadingText = document.getElementById('previewLoadingText');
            const previewLoadingSubtext = document.getElementById('previewLoadingSubtext');
            
            // Word文档处理 - 直接显示不可预览提示框
            if (fileType.includes('word') || fileExtension === 'doc' || fileExtension === 'docx') {
                loadingDialog.classList.add('hidden');
                this.showUnsupportedFileModal(filename, fileType, id);
                return true;
            }
            
            // 使用认证令牌获取文件内容
            if (previewLoadingText) previewLoadingText.textContent = '正在获取文件信息...';
            if (previewLoadingSubtext) previewLoadingSubtext.textContent = filename || '';
            
            const response = await fetch(url, {
                headers: { 
                    'Authorization': `Bearer ${token}`,
                    'Accept': 'application/json, */*' // 优先接收JSON响应
                },
                signal: abortController.signal
            });
            
            if (response.ok) {
                // 检查响应类型
                const contentType = response.headers.get('Content-Type');
                
                // 处理JSON响应
                if (contentType && contentType.includes('application/json')) {
                    if (previewLoadingText) previewLoadingText.textContent = '正在处理预览信息...';
                    
                    const jsonData = await response.json();
                    
                    // 处理不支持预览的文件类型
                    if (jsonData.previewType === 'not_supported') {
                        loadingDialog.classList.add('hidden');
                        this.showUnsupportedFileModal(jsonData.fileName || filename, jsonData.contentType || fileType, id);
                        return true;
                    } 
                    // 处理大文件预览提示
                    else if (jsonData.previewType === 'large_file') {
                        loadingDialog.classList.add('hidden');
                        this.showLargeFileModal(jsonData.fileName || filename, jsonData.fileSize, id, jsonData.message);
                        return true;
                    }
                    // 处理Office文档预览
                    else if (jsonData.previewType === 'office') {
                        // 排除Word文档，Office文档使用特定服务预览
                        if (!jsonData.fileUrl) {
                            loadingDialog.classList.add('hidden');
                            this.showUnsupportedFileModal(jsonData.fileName || filename, jsonData.contentType || fileType, id);
                            return true;
                        }
                        
                        // 使用Office Online Viewer或内部窗口预览
                        if (previewLoadingText) previewLoadingText.textContent = '正在准备Office文档预览...';
                        loadingDialog.classList.add('hidden');
                        
                        if (confirm(`使用Microsoft Office在线查看器打开"${jsonData.fileName || filename}"? \n(如果不使用，将在新窗口中查看)`)) {
                            const viewerUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(jsonData.fileUrl)}`;
                            window.open(viewerUrl, '_blank');
                        } else {
                            window.open(jsonData.fileUrl, '_blank');
                        }
                        return true;
                    }
                    // 视频预览
                    else if (jsonData.previewType === 'video' && jsonData.fileUrl) {
                        if (previewLoadingText) previewLoadingText.textContent = '准备视频播放器...';
                        loadingDialog.classList.add('hidden');
                        
                        // 调用增强版的视频播放器，使用后端返回的文件扩展名
                        const fileExt = jsonData.fileExtension || fileExtension || '';
                        this.showVideoPlayerModal(jsonData.fileName || filename, jsonData.fileUrl, id, fileExt);
                        return true;
                    }
                    // 音频预览  
                    else if (jsonData.previewType === 'audio' && jsonData.fileUrl) {
                        if (previewLoadingText) previewLoadingText.textContent = '准备音频播放器...';
                        loadingDialog.classList.add('hidden');
                        
                        // 调用增强版的音频播放器，使用后端返回的文件扩展名
                        const fileExt = jsonData.fileExtension || fileExtension || '';
                        this.showAudioPlayerModal(jsonData.fileName || filename, jsonData.fileUrl, id, fileExt);
                        return true;
                    }
                    
                    // 其他JSON响应情况
                    loadingDialog.classList.add('hidden');
                    this.showUnsupportedFileModal(jsonData.fileName || filename, jsonData.contentType || fileType, id);
                    return true;
                }
                
                // 获取文件内容并创建Blob
                if (previewLoadingText) previewLoadingText.textContent = '正在获取预览内容...';
                
                try {
                    // 使用带超时的下载机制，防止大文件预览卡死
                    const startTime = Date.now();
                    let blob;
                    
                    // 根据Content-Length判断文件大小
                    const contentLength = response.headers.get('Content-Length');
                    const fileSize = contentLength ? parseInt(contentLength, 10) : 0;
                    
                    // 超过10MB的文件使用流式下载，显示进度
                    if (fileSize > 10 * 1024 * 1024) {
                        const reader = response.body.getReader();
                        let receivedBytes = 0;
                        let chunks = [];
                        
                        // 开始读取数据流
                        while (true) {
                            try {
                                // 显示下载进度
                                if (fileSize > 0 && previewLoadingSubtext) {
                                    const progress = Math.round((receivedBytes / fileSize) * 100);
                                    previewLoadingSubtext.textContent = `正在加载 ${filename} (${progress}%)`;
                                }
                                
                                const { done, value } = await reader.read();
                                
                                if (done) {
                                    break;
                                }
                                
                                chunks.push(value);
                                receivedBytes += value.length;
                                
                                // 超过30秒，提示用户
                                const elapsedTime = Date.now() - startTime;
                                if (elapsedTime > 30000 && previewLoadingText) {
                                    previewLoadingText.textContent = '文件加载时间较长...';
                                    previewLoadingSubtext.textContent = '大文件加载可能需要较长时间，请耐心等待';
                                }
                            } catch (error) {
                                if (error.name === 'AbortError') {
                                    console.log('预览已被用户取消');
                                    return false;
                                }
                                throw error;
                            }
                        }
                        
                        // 合并所有块创建Blob
                        blob = new Blob(chunks, { type: contentType });
                    } else {
                        // 小文件直接下载
                        blob = await response.blob();
                    }
                    
                    // 隐藏加载对话框
                    loadingDialog.classList.add('hidden');
                    
                    // 使用模态窗口预览
                    this.showFilePreviewInModal(blob, filename, id, contentType || fileType);
                    
                    return true;
                } catch (error) {
                    if (error.name === 'AbortError') {
                        console.log('预览已被用户取消');
                        return false;
                    }
                    throw error;
                }
            } else if (response.status === 401) {
                // 隐藏加载对话框
                loadingDialog.classList.add('hidden');
                
                // 处理认证失败的情况
                await this.handleUnauthorizedResponse(response);
                return false;
            } else {
                // 隐藏加载对话框
                loadingDialog.classList.add('hidden');
                
                // 处理其他错误情况
                this.showUnsupportedFileModal(filename, fileType, id, "无法获取文件内容，请稍后重试");
                return false;
            }
        } catch (error) {
            console.error('预览文件出错:', error);
            
            // 隐藏加载对话框
            const loadingDialog = document.getElementById('previewLoadingDialog');
            if (loadingDialog) {
                loadingDialog.classList.add('hidden');
            }
            
            // 显示错误信息
            this.showUnsupportedFileModal(filename, fileType, id, "预览文件时出错，请稍后重试");
            return false;
        }
    }
    
    /**
     * 在模态窗口中显示文件预览
     * @param {Blob} blob - 文件Blob
     * @param {string} filename - 文件名
     * @param {number} id - 物料ID
     * @param {string} fileType - 文件类型
     */
    static showFilePreviewInModal(blob, filename, id, fileType) {
        // 创建Blob URL
        const blobUrl = URL.createObjectURL(blob);
        
        // 检查是否已经存在模态窗口
        let modal = document.getElementById('filePreviewModal');
        
        // 如果模态窗口不存在，创建一个
        if (!modal) {
            modal = document.createElement('div');
            modal.id = 'filePreviewModal';
            modal.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4 md:p-6';
            document.body.appendChild(modal);
        }
        
        // 创建模态窗口结构 - 优化布局和UI
        modal.innerHTML = `
            <div class="bg-white rounded-lg shadow-2xl w-full max-w-6xl flex flex-col h-[85vh] relative">
                <!-- 头部区域 -->
                <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center bg-gray-50 rounded-t-lg">
                    <div class="flex items-center">
                        <div class="file-icon mr-3 text-2xl"></div>
                        <h3 class="text-lg font-medium text-gray-900 truncate max-w-md">${filename || '预览文件'}</h3>
                    </div>
                    <div class="flex space-x-4 items-center">
                        <button id="fullscreenPreviewBtn" class="text-gray-500 hover:text-gray-700 transition-colors p-2 rounded-full hover:bg-gray-100">
                            <i class="fas fa-expand"></i>
                        </button>
                        <button id="downloadPreviewFile" class="text-blue-600 hover:text-blue-800 transition-colors p-2 rounded-full hover:bg-blue-50">
                            <i class="fas fa-download"></i>
                        </button>
                        <button id="closePreviewModal" class="text-gray-500 hover:text-gray-700 transition-colors p-2 rounded-full hover:bg-gray-100">
                            <i class="fas fa-times"></i>
                        </button>
                    </div>
                </div>
                
                <!-- 内容区域 -->
                <div id="previewContent" class="flex-1 overflow-auto flex items-center justify-center relative bg-gray-100">
                    <div class="absolute inset-0 flex items-center justify-center">
                        <div class="animate-spin rounded-full h-16 w-16 border-b-4 border-blue-500"></div>
                        <p class="ml-4 text-gray-500">正在加载预览...</p>
                    </div>
                </div>
                
                <!-- 底部区域 -->
                <div class="px-6 py-3 border-t border-gray-200 flex justify-between items-center bg-gray-50 rounded-b-lg">
                    <span class="text-sm text-gray-500 file-type"></span>
                </div>
            </div>
        `;
        
        // 显示模态窗口
        modal.classList.remove('hidden');
        
        // 防止页面滚动
        document.body.style.overflow = 'hidden';
        
        // 设置文件图标和类型信息
        this.setFileIconAndType(modal, fileType, filename);
        
        // 设置下载按钮事件
        const downloadBtn = document.getElementById('downloadPreviewFile');
        if (downloadBtn) {
            downloadBtn.onclick = () => {
                this.downloadMaterial(id, filename);
            };
        }
        
        // 设置全屏按钮事件
        const fullscreenBtn = document.getElementById('fullscreenPreviewBtn');
        if (fullscreenBtn) {
            fullscreenBtn.onclick = () => {
                const previewContent = document.getElementById('previewContent');
                if (!previewContent) return;
                
                if (document.fullscreenElement) {
                    document.exitFullscreen()
                        .then(() => {
                            fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i>';
                        })
                        .catch(err => {
                            console.error('退出全屏失败:', err);
                        });
                } else {
                    previewContent.requestFullscreen()
                        .then(() => {
                            fullscreenBtn.innerHTML = '<i class="fas fa-compress"></i>';
                        })
                        .catch(err => {
                            console.error('进入全屏失败:', err);
                        });
                }
            };
        }
        
        // 设置关闭按钮事件
        const closeBtn = document.getElementById('closePreviewModal');
        if (closeBtn) {
            closeBtn.onclick = () => {
                modal.classList.add('hidden');
                // 关闭时释放Blob URL
                URL.revokeObjectURL(blobUrl);
                // 恢复页面滚动
                document.body.style.overflow = '';
            };
        }
        
        // 点击蒙层关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.classList.add('hidden');
                // 关闭时释放Blob URL
                URL.revokeObjectURL(blobUrl);
                // 恢复页面滚动
                document.body.style.overflow = '';
            }
        });
        
        // 获取预览内容区域
        const previewContent = document.getElementById('previewContent');
        if (!previewContent) {
            console.error('预览内容区域不存在');
            return;
        }
        
        // 简化的文件类型检查函数
        const isType = (type) => {
            if (!fileType) return false;
            return fileType.toLowerCase().includes(type.toLowerCase());
        };
        
        const fileExtension = filename ? filename.split('.').pop().toLowerCase() : '';
        
        // 创建预览元素
        let previewElement;
        
        // 根据文件类型创建不同的预览元素
        if (isType('pdf')) {
            // PDF预览
            previewElement = document.createElement('iframe');
            previewElement.src = blobUrl;
            previewElement.className = 'w-full h-full';
            previewElement.style.border = 'none';
        }
        else if (isType('image')) {
            // 图片预览
            previewElement = document.createElement('div');
            previewElement.className = 'relative w-full h-full flex items-center justify-center bg-black bg-opacity-50 overflow-auto';
            
            const img = document.createElement('img');
            img.src = blobUrl;
            img.className = 'max-w-full max-h-full object-contain';
            img.style.margin = 'auto';
            
            // 添加图片控制工具栏
            const toolbar = document.createElement('div');
            toolbar.className = 'absolute bottom-4 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-60 rounded-full px-4 py-2 flex space-x-4';
            
            let zoomLevel = 100;
            const zoomInfo = document.createElement('span');
            zoomInfo.className = 'text-white text-sm';
            zoomInfo.textContent = '100%';
            
            // 缩小按钮
            const zoomOutBtn = document.createElement('button');
            zoomOutBtn.className = 'text-white hover:text-blue-300';
            zoomOutBtn.innerHTML = '<i class="fas fa-search-minus"></i>';
            zoomOutBtn.onclick = (e) => {
                e.stopPropagation();
                zoomLevel = Math.max(20, zoomLevel - 20);
                img.style.width = `${zoomLevel}%`;
                zoomInfo.textContent = `${zoomLevel}%`;
            };
            
            // 放大按钮
            const zoomInBtn = document.createElement('button');
            zoomInBtn.className = 'text-white hover:text-blue-300';
            zoomInBtn.innerHTML = '<i class="fas fa-search-plus"></i>';
            zoomInBtn.onclick = (e) => {
                e.stopPropagation();
                zoomLevel = Math.min(300, zoomLevel + 20);
                img.style.width = `${zoomLevel}%`;
                zoomInfo.textContent = `${zoomLevel}%`;
            };
            
            // 重置按钮
            const resetBtn = document.createElement('button');
            resetBtn.className = 'text-white hover:text-blue-300';
            resetBtn.innerHTML = '<i class="fas fa-sync-alt"></i>';
            resetBtn.onclick = (e) => {
                e.stopPropagation();
                zoomLevel = 100;
                img.style.width = '';
                zoomInfo.textContent = '100%';
            };
            
            toolbar.appendChild(zoomOutBtn);
            toolbar.appendChild(zoomInfo);
            toolbar.appendChild(zoomInBtn);
            toolbar.appendChild(resetBtn);
            
            previewElement.appendChild(img);
            previewElement.appendChild(toolbar);
        }
        else if (isType('video')) {
            // 视频预览 - 增强版
            previewElement = document.createElement('div');
            previewElement.className = 'w-full h-full flex items-center justify-center bg-black';
            
            const video = document.createElement('video');
            video.src = blobUrl;
            video.controls = true;
            video.autoplay = false;
            video.className = 'max-w-full max-h-full';
            video.style.maxHeight = '100%';
            
            previewElement.appendChild(video);
        }
        else if (isType('audio')) {
            // 音频预览 - 美化版
            previewElement = document.createElement('div');
            previewElement.className = 'w-full h-full flex flex-col items-center justify-center bg-gradient-to-b from-blue-50 to-purple-50 p-6';
            
            const audioContainer = document.createElement('div');
            audioContainer.className = 'w-full max-w-2xl bg-white p-8 rounded-xl shadow-lg';
            
            audioContainer.innerHTML = `
                <div class="mb-6 flex justify-center">
                    <div class="w-32 h-32 rounded-full bg-gradient-to-r from-blue-400 to-purple-500 flex items-center justify-center shadow-xl">
                        <i class="fas fa-music text-white text-4xl"></i>
                    </div>
                </div>
                <h3 class="text-xl font-bold text-center mb-6 text-gray-800">${filename}</h3>
                <div class="audio-player w-full">
                    <audio src="${blobUrl}" controls autoplay class="w-full"></audio>
                </div>
            `;
            
            previewElement.appendChild(audioContainer);
        }
        else if (isType('text') || isType('json') || isType('xml') || isType('javascript') || 
                fileExtension === 'txt' || fileExtension === 'csv' || fileExtension === 'md' || 
                fileExtension === 'json' || fileExtension === 'js' || fileExtension === 'html' || 
                fileExtension === 'css' || fileExtension === 'xml') {
            // 文本文件预览 - 改进版
            previewElement = document.createElement('div');
            previewElement.className = 'w-full h-full flex flex-col';
            
            // 代码编辑器样式的容器
            const editorContainer = document.createElement('div');
            editorContainer.className = 'w-full h-full bg-gray-800 overflow-auto';
            
            const textContainer = document.createElement('pre');
            textContainer.className = 'p-6 text-gray-200 whitespace-pre-wrap break-words font-mono text-sm leading-relaxed';
            editorContainer.appendChild(textContainer);
            
            // 添加行号
            const addLineNumbers = (text) => {
                const lines = text.split('\n');
                let numberedText = '';
                
                lines.forEach((line, index) => {
                    const lineNumber = index + 1;
                    numberedText += `<span class="text-gray-500 select-none mr-4 inline-block w-8 text-right">${lineNumber}</span>${line}\n`;
                });
                
                return numberedText;
            };
            
            // 读取文本内容
            const reader = new FileReader();
            reader.onload = function(e) {
                const text = e.target.result;
                // 添加行号并设置内容
                textContainer.innerHTML = addLineNumbers(text);
            };
            reader.readAsText(blob);
            
            previewElement.appendChild(editorContainer);
        }
        else {
            // 不支持预览的文件类型 - 这种情况不应该出现，因为会被previewMaterial方法过滤
            // 但以防万一，还是提供一个下载选项
            this.showUnsupportedFileModal(filename, fileType, id);
            return;
        }
        
        // 清除加载指示器
        previewContent.innerHTML = '';
        
        // 添加预览元素到内容区域
        previewContent.appendChild(previewElement);
        
        // 监听ESC键关闭模态窗口
        const handleEscKey = (e) => {
            if (e.key === 'Escape' && !document.fullscreenElement) {
                modal.classList.add('hidden');
                URL.revokeObjectURL(blobUrl);
                document.body.style.overflow = '';
                document.removeEventListener('keydown', handleEscKey);
            }
        };
        document.addEventListener('keydown', handleEscKey);
    }
    
    /**
     * 设置文件图标和类型显示
     * @param {HTMLElement} modal - 模态窗口元素
     * @param {string} fileType - 文件MIME类型
     * @param {string} filename - 文件名
     */
    static setFileIconAndType(modal, fileType, filename) {
        if (!modal) return;
        
        const iconElement = modal.querySelector('.file-icon');
        const typeElement = modal.querySelector('.file-type');
        if (!iconElement || !typeElement) return;
        
        const fileExtension = filename ? filename.split('.').pop().toLowerCase() : '';
        let iconClass = 'fas fa-file';
        let iconColor = 'text-gray-500';
        let typeText = fileType || '未知类型';
        
        // 根据文件类型设置图标和颜色
        if (fileType?.includes('pdf') || fileExtension === 'pdf') {
            iconClass = 'fas fa-file-pdf';
            iconColor = 'text-red-500';
            typeText = 'PDF文档';
        } else if (fileType?.includes('word') || fileExtension === 'doc' || fileExtension === 'docx') {
            iconClass = 'fas fa-file-word';
            iconColor = 'text-blue-500';
            typeText = 'Word文档';
        } else if (fileType?.includes('excel') || fileType?.includes('spreadsheetml') || 
                   fileExtension === 'xls' || fileExtension === 'xlsx') {
            iconClass = 'fas fa-file-excel';
            iconColor = 'text-green-500';
            typeText = 'Excel表格';
        } else if (fileType?.includes('powerpoint') || fileExtension === 'ppt' || fileExtension === 'pptx') {
            iconClass = 'fas fa-file-powerpoint';
            iconColor = 'text-orange-500';
            typeText = 'PowerPoint演示文稿';
        } else if (fileType?.includes('image')) {
            iconClass = 'fas fa-file-image';
            iconColor = 'text-purple-500';
            typeText = '图片';
        } else if (fileType?.includes('audio')) {
            iconClass = 'fas fa-file-audio';
            iconColor = 'text-yellow-500';
            typeText = '音频文件';
        } else if (fileType?.includes('video')) {
            iconClass = 'fas fa-file-video';
            iconColor = 'text-pink-500';
            typeText = '视频文件';
        } else if (fileType?.includes('zip') || fileType?.includes('rar') || fileType?.includes('archive') || 
                   fileExtension === 'zip' || fileExtension === 'rar' || fileExtension === '7z') {
            iconClass = 'fas fa-file-archive';
            iconColor = 'text-amber-500';
            typeText = '压缩文件';
        } else if (fileType?.includes('text') || fileExtension === 'txt') {
            iconClass = 'fas fa-file-alt';
            iconColor = 'text-gray-500';
            typeText = '文本文件';
        } else if (fileType?.includes('code') || fileExtension === 'js' || fileExtension === 'html' || 
                   fileExtension === 'css' || fileExtension === 'java' || fileExtension === 'py') {
            iconClass = 'fas fa-file-code';
            iconColor = 'text-indigo-500';
            typeText = '代码文件';
        }
        
        // 设置图标和类型文本
        iconElement.innerHTML = `<i class="${iconClass} ${iconColor}"></i>`;
        typeElement.textContent = typeText;
    }
    
    /**
     * 显示不支持预览的文件模态窗口
     * @param {string} filename - 文件名 
     * @param {string} fileType - 文件类型
     * @param {number} id - 物料ID
     * @param {string} message - 可选的错误消息
     */
    static showUnsupportedFileModal(filename, fileType, id, message = null) {
        // 检查是否已经存在模态窗口
        let modal = document.getElementById('filePreviewModal');
        
        // 如果模态窗口不存在，创建一个
        if (!modal) {
            modal = document.createElement('div');
            modal.id = 'filePreviewModal';
            modal.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4';
            document.body.appendChild(modal);
        }
        
        // 防止页面滚动
        document.body.style.overflow = 'hidden';
        
        // 根据文件类型获取图标
        let fileIcon = 'fa-file';
        let iconColor = 'text-gray-500';
        let typeText = '未知类型';
        
        const fileExtension = filename ? filename.split('.').pop().toLowerCase() : '';
        
        if (fileType?.includes('word') || fileExtension === 'doc' || fileExtension === 'docx') {
            fileIcon = 'fa-file-word';
            iconColor = 'text-blue-500';
            typeText = 'Word文档';
        } else if (fileType?.includes('excel') || fileType?.includes('spreadsheetml') || fileExtension === 'xls' || fileExtension === 'xlsx') {
            fileIcon = 'fa-file-excel';
            iconColor = 'text-green-500';
            typeText = 'Excel表格';
        } else if (fileType?.includes('powerpoint') || fileExtension === 'ppt' || fileExtension === 'pptx') {
            fileIcon = 'fa-file-powerpoint';
            iconColor = 'text-orange-500';
            typeText = 'PowerPoint演示文稿';
        } else if (fileType?.includes('zip') || fileType?.includes('rar') || fileType?.includes('archive') || 
                  fileExtension === 'zip' || fileExtension === 'rar' || fileExtension === '7z') {
            fileIcon = 'fa-file-archive';
            iconColor = 'text-amber-500';
            typeText = '压缩文件';
        } else if (fileType) {
            typeText = fileType;
        }
        
        // 创建模态窗口内容 - 美化版本
        modal.innerHTML = `
            <div class="bg-white rounded-lg shadow-2xl max-w-md mx-auto p-8 transform transition-all">
                <div class="text-center">
                    <div class="flex justify-center mb-6">
                        <div class="w-24 h-24 rounded-full bg-gray-100 flex items-center justify-center">
                            <i class="fas ${fileIcon} ${iconColor} text-5xl"></i>
                        </div>
                    </div>
                    <h3 class="text-xl font-bold mb-2">${filename || '未知文件'}</h3>
                    <p class="text-gray-600 mb-2">${typeText}</p>
                    <div class="my-6 bg-blue-50 border-l-4 border-blue-500 p-4 text-blue-700 rounded">
                        <p class="flex items-center">
                            <i class="fas fa-info-circle mr-2"></i>
                            <span>${message || '此文件无法在浏览器中预览，请下载后查看'}</span>
                        </p>
                    </div>
                    <div class="flex space-x-4 justify-center">
                        <button id="downloadPreviewFile" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition-colors flex items-center justify-center">
                            <i class="fas fa-download mr-2"></i>下载文件
                        </button>
                        <button id="closePreviewModal" class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-6 rounded-lg transition-colors">
                            关闭
                        </button>
                    </div>
                </div>
            </div>
        `;
        
        // 显示模态窗口
        modal.classList.remove('hidden');
        
        // 设置下载按钮事件
        const downloadBtn = document.getElementById('downloadPreviewFile');
        if (downloadBtn) {
            downloadBtn.onclick = () => {
                this.downloadMaterial(id, filename);
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 设置关闭按钮事件
        const closeBtn = document.getElementById('closePreviewModal');
        if (closeBtn) {
            closeBtn.onclick = () => {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 点击蒙层关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            }
        });
        
        // 监听ESC键关闭模态窗口
        const handleEscKey = (e) => {
            if (e.key === 'Escape') {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
                document.removeEventListener('keydown', handleEscKey);
            }
        };
        document.addEventListener('keydown', handleEscKey);
    }
    
    /**
     * 下载物料文件
     * 直接触发文件下载并处理可能的错误
     * @param {number} id - 物料ID 
     * @param {string} filename - 文件名（可选，用于显示）
     * @returns {Promise<boolean>} 是否成功开始下载
     */
    static async downloadMaterial(id, filename = '') {
        try {
            const token = sessionStorage.getItem('jwtToken');
            const url = this.getMaterialDownloadUrl(id);
            
            // 显示下载进度对话框
            let downloadProgressDialog = document.getElementById('downloadProgressDialog');
            if (!downloadProgressDialog) {
                downloadProgressDialog = document.createElement('div');
                downloadProgressDialog.id = 'downloadProgressDialog';
                downloadProgressDialog.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4';
                downloadProgressDialog.innerHTML = `
                    <div class="bg-white rounded-lg shadow-xl w-full max-w-md p-6">
                        <h3 class="text-lg font-medium text-gray-900 mb-4" id="downloadFileName">准备下载文件...</h3>
                        <div class="relative pt-1 mb-4">
                            <div class="flex mb-2 items-center justify-between">
                                <div>
                                    <span class="text-xs font-semibold inline-block py-1 px-2 uppercase rounded-full text-blue-600 bg-blue-200" id="downloadProgressText">
                                        准备中
                                    </span>
                                </div>
                                <div class="text-right">
                                    <span class="text-xs font-semibold inline-block text-blue-600" id="downloadProgressPercentage">
                                        0%
                                    </span>
                                </div>
                            </div>
                            <div class="overflow-hidden h-2 mb-4 text-xs flex rounded bg-blue-100">
                                <div id="downloadProgressBar" style="width:0%" class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-blue-500 transition-all duration-300 ease-out"></div>
                            </div>
                        </div>
                        <div class="flex justify-end space-x-3">
                            <button id="cancelDownloadBtn" class="px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300 focus:outline-none transition duration-150">
                                取消
                            </button>
                            <button id="hideDownloadProgressBtn" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 focus:outline-none transition duration-150">
                                后台下载
                            </button>
                        </div>
                    </div>
                `;
                document.body.appendChild(downloadProgressDialog);
            } else {
                downloadProgressDialog.classList.remove('hidden');
            }
            
            // 更新文件名
            const downloadFileName = document.getElementById('downloadFileName');
            if (downloadFileName) {
                downloadFileName.textContent = filename ? `正在下载: ${filename}` : '正在下载文件...';
            }
            
            // 设置取消按钮事件
            let abortController = new AbortController();
            const cancelDownloadBtn = document.getElementById('cancelDownloadBtn');
            if (cancelDownloadBtn) {
                cancelDownloadBtn.onclick = () => {
                    abortController.abort();
                    downloadProgressDialog.classList.add('hidden');
                    if (typeof window.showMessage === 'function') {
                        window.showMessage('下载已取消', '文件下载已被用户取消', 'info');
                    }
                };
            }
            
            // 设置最小化按钮事件
            const hideDownloadProgressBtn = document.getElementById('hideDownloadProgressBtn');
            if (hideDownloadProgressBtn) {
                hideDownloadProgressBtn.onclick = () => {
                    downloadProgressDialog.classList.add('hidden');
                    if (typeof window.showMessage === 'function') {
                        window.showMessage('下载继续中', '文件正在后台下载', 'info');
                    }
                };
            }
            
            // 重置进度条
            const progressBar = document.getElementById('downloadProgressBar');
            const progressText = document.getElementById('downloadProgressText');
            const progressPercentage = document.getElementById('downloadProgressPercentage');
            if (progressBar) progressBar.style.width = '0%';
            if (progressText) progressText.textContent = '连接中...';
            if (progressPercentage) progressPercentage.textContent = '0%';
            
            // 创建下载请求
            const response = await fetch(url, {
                headers: token ? { 'Authorization': `Bearer ${token}` } : {},
                signal: abortController.signal
            });
            
            if (response.ok) {
                // 获取文件名和大小
                let downloadFilename = filename;
                const contentDisposition = response.headers.get('Content-Disposition');
                if (contentDisposition) {
                    const filenameMatch = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
                    if (filenameMatch && filenameMatch[1]) {
                        downloadFilename = decodeURIComponent(filenameMatch[1]);
                    }
                }
                
                const contentLength = response.headers.get('Content-Length');
                let totalBytes = contentLength ? parseInt(contentLength, 10) : 0;
                
                // 使用ReadableStream读取响应数据并显示进度
                const reader = response.body.getReader();
                let receivedBytes = 0;
                let chunks = [];
                
                // 开始读取数据流
                while (true) {
                    try {
                        const { done, value } = await reader.read();
                        
                        if (done) {
                            break;
                        }
                        
                        chunks.push(value);
                        receivedBytes += value.length;
                        
                        // 更新进度条
                        if (totalBytes > 0) {
                            const progress = Math.round((receivedBytes / totalBytes) * 100);
                            if (progressBar) progressBar.style.width = `${progress}%`;
                            if (progressText) progressText.textContent = `下载中...`;
                            if (progressPercentage) progressPercentage.textContent = `${progress}%`;
                            
                            // 更新文件名显示
                            if (downloadFileName) {
                                const downloadedMB = (receivedBytes / (1024 * 1024)).toFixed(2);
                                const totalMB = (totalBytes / (1024 * 1024)).toFixed(2);
                                downloadFileName.textContent = `${downloadFilename} (${downloadedMB}MB / ${totalMB}MB)`;
                            }
                        } else {
                            // 对于无法获取总大小的情况，显示已下载的大小
                            if (progressText) progressText.textContent = `已下载 ${(receivedBytes / (1024 * 1024)).toFixed(2)}MB`;
                            
                            // 使用不确定进度条样式
                            if (progressBar) {
                                progressBar.style.width = '100%';
                                progressBar.classList.add('animate-pulse');
                            }
                        }
                    } catch (error) {
                        if (error.name === 'AbortError') {
                            console.log('下载已被用户取消');
                            return false;
                        }
                        throw error;
                    }
                }
                
                // 合并所有块并创建Blob
                const blob = new Blob(chunks);
                
                // 下载文件
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = downloadFilename || `文件_${id}`;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
                
                // 完成下载，更新UI
                if (progressBar) progressBar.style.width = '100%';
                if (progressText) progressText.textContent = '下载完成';
                if (progressPercentage) progressPercentage.textContent = '100%';
                
                // 短暂延迟后隐藏进度对话框
                setTimeout(() => {
                    downloadProgressDialog.classList.add('hidden');
                }, 1500);
                
                // 显示下载成功消息
                if (typeof window.showMessage === 'function') {
                    window.showMessage('下载成功', `文件${downloadFilename ? ' ' + downloadFilename : ''}已成功下载`, 'success');
                }
                
                return true;
            } else if (response.status === 401) {
                // 隐藏进度对话框
                downloadProgressDialog.classList.add('hidden');
                
                // 处理未授权错误
                this.handleUnauthorizedResponse(response);
                return false;
            } else {
                // 隐藏进度对话框
                downloadProgressDialog.classList.add('hidden');
                
                // 处理其他错误
                const errorText = await response.text();
                let errorMessage;
                try {
                    const errorJson = JSON.parse(errorText);
                    errorMessage = errorJson.message || '下载失败，请稍后重试';
                } catch (e) {
                    errorMessage = errorText || '下载失败，请稍后重试';
                }
                
                if (typeof window.showMessage === 'function') {
                    window.showMessage('下载失败', errorMessage, 'error');
                } else {
                    alert(`下载失败: ${errorMessage}`);
                }
                
                return false;
            }
        } catch (error) {
            console.error('下载文件出错:', error);
            
            // 隐藏进度对话框
            const downloadProgressDialog = document.getElementById('downloadProgressDialog');
            if (downloadProgressDialog) {
                downloadProgressDialog.classList.add('hidden');
            }

            if (typeof window.showMessage === 'function') {
                window.showMessage('下载失败', '网络错误，请检查您的网络连接', 'error');
            } else {
                alert('网络错误，请检查您的网络连接');
            }

            return false;
        }
    }
    
    /**
     * 处理未授权响应 (401错误)
     * @param {Response} response - Fetch响应对象 
     */
    static async handleUnauthorizedResponse(response) {
        let errorMessage = '您的登录已失效，请重新登录';
        let errorTitle = '登录已失效';
        let errorType = 'session_expired';
        let redirectNow = true;
        
        try {
            const responseText = await response.text();
            // 尝试解析为JSON
            try {
                const errorData = JSON.parse(responseText);
                
                // 检查是否是账号在其他地方登录
                if (errorData.error === 'token_invalidated') {
                    errorTitle = '账号已在其他设备登录';
                    errorMessage = '您的账号已在其他设备登录，请重新登录';
                    errorType = 'account_elsewhere';
                } else if (errorData.error === 'token_expired') {
                    errorTitle = '登录已过期';
                    errorMessage = '您的登录已过期，请重新登录';
                    errorType = 'token_expired';
                } else if (errorData.error === 'token_refreshed') {
                    console.warn('令牌已被刷新，正在尝试重新获取新令牌');
                    // 尝试获取新的token
                    try {
                        const refreshResult = await this.refreshToken();
                        if (refreshResult) {
                            // 如果刷新成功，重试原请求
                            console.info('令牌刷新成功，重试原请求');
                            // 重新构建选项，使用新token
                            const newToken = sessionStorage.getItem('jwtToken');
                            options.headers['Authorization'] = `Bearer ${newToken}`;
                            // 重试请求
                            const retryResponse = await fetch(url, options);
                            return retryResponse;
                        } else {
                            // 刷新失败，显示错误
                            errorTitle = '会话已刷新失败';
                            errorMessage = '无法恢复您的会话，请重新登录';
                            errorType = 'refresh_failed';
                        }
                    } catch (refreshError) {
                        console.error('刷新令牌失败:', refreshError);
                        errorTitle = '会话恢复失败';
                        errorMessage = '会话恢复失败，请重新登录';
                        errorType = 'refresh_failed';
                    }
                } else {
                    errorTitle = '登录已失效';
                    errorMessage = '您的登录状态已失效，请重新登录';
                    errorType = 'token_invalid';
                }
            } catch (e) {
                // 如果不是JSON，使用通用错误消息
                console.error('无法解析服务器响应:', responseText);
            }
        } catch (e) {
            console.error('读取服务器响应失败:', e);
        }
        
        // 保存错误信息到会话存储，用于登录页面显示
        sessionStorage.setItem('loginErrorReason', errorMessage);
        
        // 使用增强的自定义消息提示（如果可用）
        if (typeof window.showAuthModal === 'function') {
            // 使用新的增强版认证弹窗
            window.showAuthModal(errorTitle, errorMessage, errorType);
            redirectNow = false;
        }
        // 兼容旧版消息弹窗
        else if (typeof window.showMessage === 'function') {
            window.showMessage(errorTitle, errorMessage, 'error');
            
            // 设置短暂延迟后再重定向，让用户有时间看到消息
            redirectNow = false;
            setTimeout(() => {
                // 清除会话存储中的登录信息
                this.clearAuthSession();
                
                // 重定向到登录页
                window.location.href = '/login.html';
            }, 3000); // 3秒后重定向，增加时间让用户看清提示
        }
        
        // 如果没有自定义消息弹窗，立即重定向
        if (redirectNow) {
            // 清除会话存储中的登录信息
            this.clearAuthSession();
            
            // 重定向到登录页
            window.location.href = '/login.html';
        }
    }
    
    /**
     * 显示大文件提示模态窗口
     * @param {string} filename - 文件名
     * @param {string} fileSize - 文件大小描述（如"50MB"）
     * @param {number} id - 物料ID
     * @param {string} message - 提示消息
     */
    static showLargeFileModal(filename, fileSize, id, message = null) {
        // 创建或获取模态窗口
        let modal = document.getElementById('filePreviewModal');
        if (!modal) {
            modal = document.createElement('div');
            modal.id = 'filePreviewModal';
            modal.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4 md:p-6';
            document.body.appendChild(modal);
        }
        
        // 获取文件扩展名
        const fileExtension = filename ? filename.split('.').pop().toLowerCase() : '';
        
        // 创建模态窗口内容
        modal.innerHTML = `
            <div class="bg-white rounded-lg shadow-xl p-6 max-w-lg w-full">
                <div class="flex items-center justify-between mb-4 pb-3 border-b border-gray-200">
                    <div class="flex items-center">
                        <div class="text-blue-500 text-4xl mr-4">
                            <i class="fas fa-file-alt"></i>
                        </div>
                        <h3 class="text-xl font-medium text-gray-900 truncate max-w-xs">${filename || '大文件'}</h3>
                    </div>
                    <button id="closePreviewModal" class="text-gray-400 hover:text-gray-600 transition-colors">
                        <i class="fas fa-times text-xl"></i>
                    </button>
                </div>
                <div class="mb-6">
                    <div class="text-center p-4 mb-4 bg-blue-50 rounded-lg">
                        <i class="fas fa-exclamation-circle text-blue-500 text-4xl mb-2"></i>
                        <p class="text-blue-700 font-medium">${message || `文件较大（${fileSize}），建议下载后查看`}</p>
                    </div>
                    <div class="text-gray-600 text-sm space-y-2">
                        <p><i class="fas fa-info-circle mr-1"></i> 大文件在浏览器中预览可能会导致性能问题</p>
                        <p><i class="fas fa-info-circle mr-1"></i> 建议下载文件后使用专用软件打开</p>
                    </div>
                </div>
                <div class="flex justify-end space-x-3">
                    <button id="closePreviewBtn" class="px-4 py-2 text-gray-700 bg-gray-200 rounded hover:bg-gray-300 transition-colors">
                        取消
                    </button>
                    <button id="downloadPreviewFile" class="px-4 py-2 text-white bg-blue-600 rounded hover:bg-blue-700 transition-colors">
                        下载文件
                    </button>
                </div>
            </div>
        `;
        
        // 显示模态窗口
        modal.classList.remove('hidden');
        document.body.style.overflow = 'hidden';
        
        // 设置下载按钮事件
        const downloadBtn = document.getElementById('downloadPreviewFile');
        if (downloadBtn) {
            downloadBtn.onclick = () => {
                this.downloadMaterial(id, filename);
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 设置关闭按钮事件（X按钮）
        const closeBtn = document.getElementById('closePreviewModal');
        if (closeBtn) {
            closeBtn.onclick = () => {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 设置取消按钮事件（改用closePreviewBtn而非cancelPreviewBtn）
        const cancelBtn = document.getElementById('closePreviewBtn');
        if (cancelBtn) {
            cancelBtn.onclick = () => {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 点击蒙层关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            }
        });
        
        // 监听ESC键关闭模态窗口
        const handleEscKey = (e) => {
            if (e.key === 'Escape') {
                modal.classList.add('hidden');
                document.body.style.overflow = '';
                document.removeEventListener('keydown', handleEscKey);
            }
        };
        document.addEventListener('keydown', handleEscKey);
    }
    
    /**
     * 显示视频播放器模态窗口
     * @param {string} filename - 文件名
     * @param {string} fileUrl - 视频文件URL
     * @param {number} id - 物料ID
     * @param {string} fileExtension - 文件扩展名（可选）
     */
    static showVideoPlayerModal(filename, fileUrl, id, fileExtension = '') {
        // 创建或获取模态窗口
        let modal = document.getElementById('filePreviewModal');
        if (!modal) {
            modal = document.createElement('div');
            modal.id = 'filePreviewModal';
            modal.className = 'fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 p-2 md:p-4';
            document.body.appendChild(modal);
        }
        
        // 获取文件扩展名（如果未提供，则从文件名中提取）
        const ext = fileExtension || (filename ? filename.split('.').pop().toLowerCase() : '');
        
        // 创建模态窗口内容
        modal.innerHTML = `
            <div class="bg-gray-900 rounded-lg shadow-2xl w-full max-w-6xl flex flex-col h-[90vh]">
                <div class="flex justify-between items-center p-4 bg-gray-800 rounded-t-lg text-white">
                    <div class="flex items-center">
                        <i class="fas fa-play-circle text-2xl mr-3 text-blue-400"></i>
                        <h3 class="text-lg font-medium truncate max-w-md">${filename || '视频预览'}</h3>
                    </div>
                    <div class="flex space-x-3">
                        <button id="fullscreenPreviewBtn" class="p-2 text-gray-300 hover:text-white rounded-full hover:bg-gray-700">
                            <i class="fas fa-expand"></i>
                        </button>
                        <button id="downloadPreviewFile" class="p-2 text-gray-300 hover:text-white rounded-full hover:bg-gray-700">
                            <i class="fas fa-download"></i>
                        </button>
                        <button id="closePreviewModal" class="p-2 text-gray-300 hover:text-white rounded-full hover:bg-gray-700">
                            <i class="fas fa-times"></i>
                        </button>
                    </div>
                </div>
                <div class="flex-1 flex items-center justify-center bg-black overflow-hidden relative">
                    <div class="absolute inset-0 flex items-center justify-center" id="videoLoadingIndicator">
                        <div class="animate-spin rounded-full h-16 w-16 border-4 border-blue-500 border-t-transparent"></div>
                        <p class="ml-4 text-gray-300">加载视频中...</p>
                    </div>
                    <video id="videoPlayer" class="max-h-full max-w-full hidden" controls autoplay controlsList="nodownload">
                        <source src="${fileUrl}" type="video/mp4">
                        您的浏览器不支持HTML5视频播放。
                    </video>
                </div>
            </div>
        `;
        
        // 显示模态窗口
        modal.classList.remove('hidden');
        document.body.style.overflow = 'hidden';
        
        // 设置下载按钮事件
        const downloadBtn = document.getElementById('downloadPreviewFile');
        if (downloadBtn) {
            downloadBtn.onclick = () => {
                this.downloadMaterial(id, filename);
            };
        }
        
        // 设置全屏按钮事件
        const fullscreenBtn = document.getElementById('fullscreenPreviewBtn');
        if (fullscreenBtn) {
            fullscreenBtn.onclick = () => {
                const videoPlayer = document.getElementById('videoPlayer');
                if (!videoPlayer) return;
                
                if (document.fullscreenElement) {
                    document.exitFullscreen();
                } else {
                    videoPlayer.requestFullscreen();
                }
            };
        }
        
        // 设置关闭按钮事件
        const closeBtn = document.getElementById('closePreviewModal');
        if (closeBtn) {
            closeBtn.onclick = () => {
                // 暂停视频播放
                const videoPlayer = document.getElementById('videoPlayer');
                if (videoPlayer) {
                    videoPlayer.pause();
                }
                
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 点击蒙层关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                // 暂停视频播放
                const videoPlayer = document.getElementById('videoPlayer');
                if (videoPlayer) {
                    videoPlayer.pause();
                }
                
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            }
        });
        
        // 监听ESC键关闭模态窗口
        const handleEscKey = (e) => {
            if (e.key === 'Escape') {
                // 暂停视频播放
                const videoPlayer = document.getElementById('videoPlayer');
                if (videoPlayer) {
                    videoPlayer.pause();
                }
                
                modal.classList.add('hidden');
                document.body.style.overflow = '';
                document.removeEventListener('keydown', handleEscKey);
            }
        };
        document.addEventListener('keydown', handleEscKey);
        
        // 根据视频文件扩展名调整source类型
        const videoPlayer = document.getElementById('videoPlayer');
        if (videoPlayer) {
            const sourceElement = videoPlayer.querySelector('source');
            if (sourceElement) {
                let mimeType = 'video/mp4';
                if (ext === 'webm') {
                    mimeType = 'video/webm';
                } else if (ext === 'ogg' || ext === 'ogv') {
                    mimeType = 'video/ogg';
                } else if (ext === 'mov') {
                    mimeType = 'video/quicktime';
                } else if (ext === 'avi') {
                    mimeType = 'video/x-msvideo';
                }
                sourceElement.type = mimeType;
                
                // 视频加载事件
                videoPlayer.onloadeddata = function() {
                    // 隐藏加载指示器
                    const loadingIndicator = document.getElementById('videoLoadingIndicator');
                    if (loadingIndicator) {
                        loadingIndicator.classList.add('hidden');
                    }
                    // 显示视频
                    videoPlayer.classList.remove('hidden');
                };
                
                // 视频加载错误处理
                videoPlayer.onerror = function() {
                    console.error('视频加载失败:', videoPlayer.error);
                    const loadingIndicator = document.getElementById('videoLoadingIndicator');
                    if (loadingIndicator) {
                        loadingIndicator.innerHTML = `
                            <div class="text-center p-4">
                                <i class="fas fa-exclamation-triangle text-yellow-500 text-4xl mb-2"></i>
                                <p class="text-white">视频加载失败，请尝试下载后查看</p>
                            </div>
                        `;
                    }
                };
                
                // 尝试加载视频
                videoPlayer.load();
            }
        }
    }
    
    /**
     * 显示音频播放器模态窗口
     * @param {string} filename - 文件名
     * @param {string} fileUrl - 音频文件URL
     * @param {number} id - 物料ID
     * @param {string} fileExtension - 文件扩展名（可选）
     */
    static showAudioPlayerModal(filename, fileUrl, id, fileExtension = '') {
        // 创建或获取模态窗口
        let modal = document.getElementById('filePreviewModal');
        if (!modal) {
            modal = document.createElement('div');
            modal.id = 'filePreviewModal';
            modal.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50 p-4 md:p-6';
            document.body.appendChild(modal);
        }
        
        // 获取文件扩展名（如果未提供，则从文件名中提取）
        const ext = fileExtension || (filename ? filename.split('.').pop().toLowerCase() : '');
        
        // 创建模态窗口内容
        modal.innerHTML = `
            <div class="bg-white rounded-lg shadow-xl p-6 max-w-lg w-full">
                <div class="flex items-center justify-between mb-6 pb-3 border-b border-gray-200">
                    <div class="flex items-center">
                        <div class="text-blue-500 text-4xl mr-4">
                            <i class="fas fa-music"></i>
                        </div>
                        <h3 class="text-xl font-medium text-gray-900 truncate max-w-xs">${filename || '音频预览'}</h3>
                    </div>
                    <button id="closePreviewModal" class="text-gray-400 hover:text-gray-600 transition-colors">
                        <i class="fas fa-times text-xl"></i>
                    </button>
                </div>
                <div class="mb-6 bg-gray-100 p-4 rounded-lg">
                    <audio id="audioPlayer" class="w-full" controls autoplay controlsList="nodownload">
                        <source src="${fileUrl}" type="audio/mpeg">
                        您的浏览器不支持HTML5音频播放。
                    </audio>
                </div>
                <div class="flex justify-end space-x-3">
                    <button id="downloadPreviewFile" class="px-4 py-2 text-white bg-blue-600 rounded hover:bg-blue-700 transition-colors">
                        下载文件
                    </button>
                </div>
            </div>
        `;
        
        // 显示模态窗口
        modal.classList.remove('hidden');
        document.body.style.overflow = 'hidden';
        
        // 设置下载按钮事件
        const downloadBtn = document.getElementById('downloadPreviewFile');
        if (downloadBtn) {
            downloadBtn.onclick = () => {
                this.downloadMaterial(id, filename);
            };
        }
        
        // 设置关闭按钮事件
        const closeBtn = document.getElementById('closePreviewModal');
        if (closeBtn) {
            closeBtn.onclick = () => {
                // 暂停音频播放
                const audioPlayer = document.getElementById('audioPlayer');
                if (audioPlayer) {
                    audioPlayer.pause();
                }
                
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            };
        }
        
        // 点击蒙层关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                // 暂停音频播放
                const audioPlayer = document.getElementById('audioPlayer');
                if (audioPlayer) {
                    audioPlayer.pause();
                }
                
                modal.classList.add('hidden');
                document.body.style.overflow = '';
            }
        });
        
        // 监听ESC键关闭模态窗口
        const handleEscKey = (e) => {
            if (e.key === 'Escape') {
                // 暂停音频播放
                const audioPlayer = document.getElementById('audioPlayer');
                if (audioPlayer) {
                    audioPlayer.pause();
                }
                
                modal.classList.add('hidden');
                document.body.style.overflow = '';
                document.removeEventListener('keydown', handleEscKey);
            }
        };
        document.addEventListener('keydown', handleEscKey);
        
        // 根据音频文件扩展名调整source类型
        const audioPlayer = document.getElementById('audioPlayer');
        if (audioPlayer) {
            const sourceElement = audioPlayer.querySelector('source');
            if (sourceElement) {
                let mimeType = 'audio/mpeg';
                if (ext === 'ogg') {
                    mimeType = 'audio/ogg';
                } else if (ext === 'wav') {
                    mimeType = 'audio/wav';
                }
                sourceElement.type = mimeType;
            }
        }
    }
    
    /**
     * 文档合并相关API
     */
    
    /**
     * 检查Python文档处理服务状态
     */
    static async checkPythonServiceStatus() {
        const response = await this.fetchWithToken('/api/document/python-status');
        if (response && response.ok) {
            return await response.json();
        }
        throw new Error('获取Python服务状态失败');
    }
    
    /**
     * 发送重启Python服务请求
     */
    static async restartPythonService() {
        const response = await this.fetchWithToken('/api/document/restart-python');
        if (response && response.ok) {
            return await response.text();
        }
        throw new Error('重启Python服务请求失败');
    }
    
    /**
     * 清理临时文件和过期任务
     */
    static async cleanupDocumentTempFiles() {
        const response = await this.fetchWithToken('/api/document/cleanup', {
            method: 'POST'
        });
        if (response && response.ok) {
            return await response.json();
        }
        throw new Error('清理临时文件请求失败');
    }
    
    /**
     * 合并文档
     * @param {FormData} formData - 包含文件和合并选项的表单数据
     * @returns {Promise<Object>} 包含任务ID的响应对象
     */
    static async mergeDocuments(formData) {
        try {
            const response = await this.fetchWithToken('/api/document/merge', {
                method: 'POST',
                body: formData
            });
            
            if (!response) {
                throw new Error('网络连接失败，请检查您的网络连接');
            }
            
            // 处理503错误（服务繁忙）
            if (response.status === 503) {
                const errorData = await response.json();
                const error = new Error(errorData.message || '后台处理服务暂时不可用，请稍后再试');
                error.status = 503;
                error.response = response;
                throw error;
            }
            
            if (!response.ok) {
                const errorData = await response.json();
                const error = new Error(errorData.message || '合并请求失败');
                error.status = response.status;
                error.response = response;
                throw error;
            }
            
            return response.json();
        } catch (error) {
            console.error('合并请求错误:', error);
            
            // 检查是否是服务不可用
            if (error.message && (
                error.message.includes('Connection refused') || 
                error.message.includes('连接被拒绝') ||
                error.message.includes('后台处理服务暂时不可用')
            )) {
                const enhancedError = new Error('后台处理服务暂时不可用，请稍后再试');
                enhancedError.status = 503;
                enhancedError.originalError = error;
                throw enhancedError;
            }
            
            throw error;
        }
    }
    
    /**
     * 获取任务状态
     * @param {string} taskId - 任务ID
     * @returns {Promise<Object>} 任务状态信息
     */
    static async getTaskStatus(taskId) {
        try {
            if (!taskId || taskId === 'null' || taskId === 'undefined') {
                throw new Error('任务ID无效，请重新上传文件');
            }
            
            const response = await this.fetchWithToken(`/api/document/task/${taskId}`, {
                method: 'GET'
            });
            
            if (!response) {
                throw new Error('网络连接失败，请检查您的网络连接');
            }
            
            // 处理503错误（服务繁忙）
            if (response.status === 503) {
                const errorData = await response.json();
                const error = new Error(errorData.message || '后台处理服务暂时不可用，请稍后再试');
                error.status = 503;
                error.response = response;
                throw error;
            }
            
            if (!response.ok) {
                const errorData = await response.json();
                const error = new Error(errorData.message || '获取任务状态失败');
                error.status = response.status;
                error.response = response;
                throw error;
            }
            
            return response.json();
        } catch (error) {
            console.error('获取任务状态错误:', error);
            
            // 检查是否是服务不可用或连接错误
            if (error.message && (
                error.message.includes('Connection refused') || 
                error.message.includes('连接被拒绝') ||
                error.message.includes('后台处理服务暂时不可用') ||
                error.message.includes('I/O error') ||
                error.message.includes('getsockopt')
            )) {
                const enhancedError = new Error('后台处理服务暂时不可用，请稍后再试');
                enhancedError.status = 503;
                enhancedError.originalError = error;
                throw enhancedError;
            }
            
            throw error;
        }
    }
    
    /**
     * 下载合并后的文档
     * @param {string} taskId - 任务ID
     * @returns {Promise<void>}
     */
    static async downloadMergedDocument(taskId) {
        try {
            if (!taskId || taskId === 'null' || taskId === 'undefined') {
                throw new Error('任务ID无效，请重新上传文件');
            }
            
            // 显示下载进度对话框
            let downloadProgressDialog = document.getElementById('downloadProgressDialog');
            if (!downloadProgressDialog) {
                downloadProgressDialog = document.createElement('div');
                downloadProgressDialog.id = 'downloadProgressDialog';
                downloadProgressDialog.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4';
                downloadProgressDialog.innerHTML = `
                    <div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md">
                        <div class="flex items-center justify-between mb-4">
                            <h3 class="text-lg font-medium text-gray-900" id="downloadFileName">正在下载文件...</h3>
                            <div class="flex space-x-2">
                                <button id="hideDownloadProgressBtn" class="text-gray-400 hover:text-gray-500">
                                    <i class="fas fa-minus"></i>
                                </button>
                                <button id="cancelDownloadBtn" class="text-gray-400 hover:text-gray-500">
                                    <i class="fas fa-times"></i>
                                </button>
                            </div>
                        </div>
                        <div class="space-y-4">
                            <div class="w-full bg-gray-200 rounded-full h-2.5">
                                <div id="downloadProgressBar" class="bg-blue-600 h-2.5 rounded-full transition-all duration-300" style="width: 0%"></div>
                            </div>
                            <div class="flex justify-between text-sm text-gray-500">
                                <span id="downloadProgressText">连接中...</span>
                                <span id="downloadProgressPercentage">0%</span>
                            </div>
                        </div>
                    </div>
                `;
                document.body.appendChild(downloadProgressDialog);
            } else {
                downloadProgressDialog.classList.remove('hidden');
            }

            // 设置取消按钮事件
            let abortController = new AbortController();
            const cancelDownloadBtn = document.getElementById('cancelDownloadBtn');
            if (cancelDownloadBtn) {
                cancelDownloadBtn.onclick = () => {
                    abortController.abort();
                    downloadProgressDialog.classList.add('hidden');
                    if (typeof window.showMessage === 'function') {
                        window.showMessage('下载已取消', '文件下载已被用户取消', 'info');
                    }
                };
            }

            // 设置最小化按钮事件
            const hideDownloadProgressBtn = document.getElementById('hideDownloadProgressBtn');
            if (hideDownloadProgressBtn) {
                hideDownloadProgressBtn.onclick = () => {
                    downloadProgressDialog.classList.add('hidden');
                    if (typeof window.showMessage === 'function') {
                        window.showMessage('下载继续中', '文件正在后台下载', 'info');
                    }
                };
            }

            // 重置进度条
            const progressBar = document.getElementById('downloadProgressBar');
            const progressText = document.getElementById('downloadProgressText');
            const progressPercentage = document.getElementById('downloadProgressPercentage');
            if (progressBar) progressBar.style.width = '0%';
            if (progressText) progressText.textContent = '连接中...';
            if (progressPercentage) progressPercentage.textContent = '0%';

            const response = await this.fetchWithToken(`/api/document/download-merged/${taskId}`, {
                method: 'GET',
                signal: abortController.signal
            });
            
            // 检查响应状态
            if (!response) {
                throw new Error('网络连接失败，请检查您的网络连接');
            }
            
            // 检查是否服务不可用
            if (response.status === 503) {
                // 尝试解析服务器返回的错误消息
                try {
                    const errorJson = await response.clone().json();
                    const errorMsg = errorJson.message || '后台处理服务暂时不可用，请稍后再试';
                    const error = new Error(errorMsg);
                    error.status = 503;
                    throw error;
                } catch (e) {
                    // 如果不是JSON或解析失败，使用通用错误消息
                    const error = new Error('后台处理服务暂时不可用，请稍后再试');
                    error.status = 503;
                    throw error;
                }
            }
            
            // 检查其他错误状态
            if (!response.ok) {
                // 尝试解析错误信息
                try {
                    const errorJson = await response.clone().json();
                    throw new Error(errorJson.message || `下载失败 (${response.status})`);
                } catch (e) {
                    // 如果不是JSON或解析失败，使用通用错误消息
                    throw new Error(`下载失败：${response.statusText || '服务器错误'}`);
                }
            }

            // 获取文件名
            const contentDisposition = response.headers.get('Content-Disposition');
            let filename = 'merged_document.pdf';
            if (contentDisposition) {
                const filenameMatch = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
                if (filenameMatch && filenameMatch[1]) {
                    filename = decodeURIComponent(filenameMatch[1]);
                }
            }

            // 更新文件名显示
            const downloadFileName = document.getElementById('downloadFileName');
            if (downloadFileName) {
                downloadFileName.textContent = `正在下载: ${filename}`;
            }

            // 获取文件大小
            const contentLength = response.headers.get('Content-Length');
            let totalBytes = contentLength ? parseInt(contentLength, 10) : 0;

            // 使用ReadableStream读取响应数据并显示进度
            const reader = response.body.getReader();
            let receivedBytes = 0;
            let chunks = [];

            // 开始读取数据流
            while (true) {
                try {
                    const { done, value } = await reader.read();

                    if (done) {
                        break;
                    }

                    chunks.push(value);
                    receivedBytes += value.length;

                    // 更新进度条
                    if (totalBytes > 0) {
                        const progress = Math.round((receivedBytes / totalBytes) * 100);
                        if (progressBar) progressBar.style.width = `${progress}%`;
                        if (progressText) progressText.textContent = `下载中...`;
                        if (progressPercentage) progressPercentage.textContent = `${progress}%`;

                        // 更新文件名显示
                        if (downloadFileName) {
                            const downloadedMB = (receivedBytes / (1024 * 1024)).toFixed(2);
                            const totalMB = (totalBytes / (1024 * 1024)).toFixed(2);
                            downloadFileName.textContent = `${filename} (${downloadedMB}MB / ${totalMB}MB)`;
                        }
                    } else {
                        // 对于无法获取总大小的情况，显示已下载的大小
                        if (progressText) progressText.textContent = `已下载 ${(receivedBytes / (1024 * 1024)).toFixed(2)}MB`;

                        // 使用不确定进度条样式
                        if (progressBar) {
                            progressBar.style.width = '100%';
                            progressBar.classList.add('animate-pulse');
                        }
                    }
                } catch (error) {
                    if (error.name === 'AbortError') {
                        console.log('下载已被用户取消');
                        return false;
                    }
                    throw error;
                }
            }

            // 合并所有块并创建Blob
            const blob = new Blob(chunks);
            const url = window.URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.href = url;
            link.download = filename;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            window.URL.revokeObjectURL(url);

            // 完成下载，更新UI
            if (progressBar) progressBar.style.width = '100%';
            if (progressText) progressText.textContent = '下载完成';
            if (progressPercentage) progressPercentage.textContent = '100%';

            // 短暂延迟后隐藏进度对话框
            setTimeout(() => {
                downloadProgressDialog.classList.add('hidden');
            }, 1500);

            // 显示下载成功消息
            if (typeof window.showMessage === 'function') {
                window.showMessage('下载成功', `文件${filename}已成功下载`, 'success');
            }

            return true;
        } catch (error) {
            console.error('下载失败:', error);
            
            // 隐藏进度对话框
            const downloadProgressDialog = document.getElementById('downloadProgressDialog');
            if (downloadProgressDialog) {
                downloadProgressDialog.classList.add('hidden');
            }
            
            // 检查是否是服务不可用错误
            if (error.status === 503 || 
                (error.message && (
                    error.message.includes('后台处理服务暂时不可用') ||
                    error.message.includes('Connection refused') ||
                    error.message.includes('连接被拒绝') ||
                    error.message.includes('I/O error') ||
                    error.message.includes('getsockopt')
                ))
            ) {
                // 包装为503错误并向上抛出，让前端友好处理
                const enhancedError = new Error('后台处理服务暂时不可用，请稍后再试');
                enhancedError.status = 503;
                enhancedError.originalError = error;
                throw enhancedError;
            }

            // 对于其他错误，显示消息并向上抛出
            if (typeof window.showMessage === 'function') {
                window.showMessage('下载失败', error.message || '下载文件时出错，请重试', 'error');
            } else {
                alert(error.message || '下载文件时出错，请重试');
            }
            
            throw error;
        }
    }

    /**
     * 公告相关接口
     */

    /**
     * 获取公告列表
     * @returns {Promise<Array>} 公告列表
     */
    static async getNotices() {
        try {
            const response = await this.fetchWithToken('/api/notice/list');
            if (response && response.ok) {
                return await response.json();
            }
            return [];
        } catch (error) {
            console.error('获取公告列表出错:', error);
            throw error;
        }
    }

    /**
     * 获取公告详情
     * @param {string} noticeId 公告ID
     * @returns {Promise<Object>} 公告详情
     */
    static async getNotice(noticeId) {
        try {
            const response = await this.fetchWithToken(`/api/notice/${noticeId}`);
            if (response && response.ok) {
                return await response.json();
            }
            return null;
        } catch (error) {
            console.error('获取公告详情出错:', error);
            throw error;
        }
    }

    /**
     * 添加公告
     * @param {string} title 公告标题
     * @param {string} content 公告内容
     * @param {string} type 公告类型
     * @returns {Promise<Object>} 添加结果
     */
    static async addNotice(title, content, type) {
        try {
            const response = await this.fetchWithToken('/api/notice/add', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    title,
                    content,
                    type
                })
            });
            
            if (response && response.ok) {
                return await response.json();
            }
            
            return {
                success: false,
                message: response.statusText || '添加公告失败'
            };
        } catch (error) {
            console.error('添加公告出错:', error);
            throw error;
        }
    }

    /**
     * 更新公告
     * @param {string} noticeId 公告ID
     * @param {string} title 公告标题
     * @param {string} content 公告内容
     * @param {string} type 公告类型
     * @returns {Promise<Object>} 更新结果
     */
    static async updateNotice(noticeId, title, content, type) {
        try {
            const response = await this.fetchWithToken(`/api/notice/update/${noticeId}`, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    title,
                    content,
                    type
                })
            });
            
            if (response && response.ok) {
                return await response.json();
            }
            
            return {
                success: false,
                message: response.statusText || '更新公告失败'
            };
        } catch (error) {
            console.error('更新公告出错:', error);
            throw error;
        }
    }

    /**
     * 删除公告
     * @param {string} noticeId 公告ID
     * @returns {Promise<Object>} 删除结果
     */
    static async deleteNotice(noticeId) {
        try {
            const response = await this.fetchWithToken(`/api/notice/delete/${noticeId}`, {
                method: 'DELETE'
            });
            
            if (response && response.ok) {
                return {
                    success: true,
                    message: '删除成功'
                };
            }
            
            return {
                success: false,
                message: response.statusText || '删除公告失败'
            };
        } catch (error) {
            console.error('删除公告出错:', error);
            throw error;
        }
    }

    /**
     * 获取用户总数（只在拥有权限时使用）
     * @returns {Promise<number>} 用户总数
     */
    static async getUsersCount() {
        try {
            // 调用新的API端点获取用户总数
            const response = await this.fetchWithToken('/api/users/count');
            if (response && response.ok) {
                const data = await response.json();
                if (data && typeof data.count === 'number') {
                    return data.count;
                }
            }
            
            // 如果新API不可用，尝试管理员API（向下兼容）
            if (this.isAdmin()) {
                const response = await this.fetchWithToken('/api/admin/users');
                if (response && response.ok) {
                    const users = await response.json();
                    if (Array.isArray(users)) {
                        return users.length;
                    }
                }
            }
            
            // 如果两种方法都失败，返回默认值
            return 2;
        } catch (error) {
            console.error('获取用户总数失败:', error);
            return 2; // 如果出错，返回已知的用户数量
        }
    }

    /**
     * 清理孤立的物料文件
     * 删除物理存在但数据库中没有记录的文件
     * 只有管理员才能执行此操作
     * 
     * @returns {Promise<Object>} - 清理结果
     */
    static async cleanupOrphanedFiles() {
        const response = await this.fetchWithToken('/api/materials/cleanup', {
            method: 'POST'
        });
        
        if (response && response.ok) {
            return await response.json();
        }
        return null;
    }

    /**
     * 标记公告为已读
     * @param {string} noticeId 公告ID
     * @returns {Promise<Object>} 标记结果
     */
    static async markNoticeAsRead(noticeId) {
        try {
            const response = await this.fetchWithToken(`/api/notice-read/mark/${noticeId}`, {
                method: 'POST'
            });
            
            if (response && response.ok) {
                return await response.json();
            }
            
            return {
                success: false,
                message: response.statusText || '标记公告已读失败'
            };
        } catch (error) {
            console.error('标记公告已读出错:', error);
            throw error;
        }
    }

    /**
     * 获取公告的已读记录
     * @param {string} noticeId 公告ID
     * @returns {Promise<Array>} 已读记录列表
     */
    static async getNoticeReadRecords(noticeId) {
        try {
            const response = await this.fetchWithToken(`/api/notice-read/records/${noticeId}`);
            
            if (response && response.ok) {
                return await response.json();
            }
            
            return [];
        } catch (error) {
            console.error('获取公告已读记录出错:', error);
            throw error;
        }
    }

    /**
     * 检查用户是否已读某公告
     * @param {string} noticeId 公告ID
     * @returns {Promise<boolean>} 是否已读
     */
    static async checkNoticeRead(noticeId) {
        try {
            const response = await this.fetchWithToken(`/api/notice-read/check/${noticeId}`);
            
            if (response && response.ok) {
                const result = await response.json();
                return result.hasRead;
            }
            
            return false;
        } catch (error) {
            console.error('检查公告已读状态出错:', error);
            return false;
        }
    }

    /**
     * 获取当前用户未读的公告数量
     * @returns {Promise<number>} 未读公告数量
     */
    static async getUnreadNoticeCount() {
        try {
            const response = await this.fetchWithToken('/api/notice-read/unread-count');
            
            if (response && response.ok) {
                const result = await response.json();
                return result.count;
            }
            
            return 0;
        } catch (error) {
            console.error('获取未读公告数量出错:', error);
            return 0;
        }
    }

    /**
     * 获取公告的未读用户列表（管理员权限）
     * @param {string} noticeId 公告ID
     * @returns {Promise<Array>} 未读用户列表
     */
    static async getNoticeUnreadUsers(noticeId) {
        try {
            // 检查是否为管理员
            if (!this.isAdmin()) {
                console.warn('非管理员无法获取未读用户列表');
                return [];
            }
            
            console.log(`尝试获取公告ID=${noticeId}的未读用户列表`);
            const response = await this.fetchWithToken(`/api/notice-read/unread-users/${noticeId}`);
            
            if (response && response.ok) {
                const data = await response.json();
                console.log(`成功获取未读用户列表，共${data.length}条记录:`, data);
                return data;
            } else {
                console.error(`获取未读用户列表失败，状态码: ${response.status}，状态文本: ${response.statusText}`);
                if (response) {
                    try {
                        const errorText = await response.text();
                        console.error('错误详情:', errorText);
                    } catch (e) {
                        console.error('无法读取错误详情');
                    }
                }
            }
            
            return [];
        } catch (error) {
            console.error('获取公告未读用户列表出错:', error);
            return [];
        }
    }
} 